import { camelCase, upperFirst, keys, trim } from "lodash-es";
import cheerio, { Cheerio, CheerioAPI, Node } from "cheerio";
import fetch from "node-fetch";
import { writeFileSync } from "fs";

const upperCamelCase = (s: string) => upperFirst(camelCase(s));

type Dictionary<T> = { [k: string]: T };

const load = async (url: string) => {
  url = `https://now-proxy-3.vercel.app/${url}`;
  console.log(`fetching ${url}`);
  const content = await fetch(url).then((resp) => resp.text());
  return cheerio.load(content);
};

const createImageCollector = () => {
  const images: Dictionary<string> = {};
  const actions: Dictionary<Array<($: CheerioAPI) => void | Promise<void>>> = {};

  return {
    register(cat: string, name: string, url: string) {
      images[`${cat}__${upperCamelCase(name)}`] = url;
    },

    page(url: string, fn: ($: CheerioAPI) => void | Promise<void>) {
      actions[url] = [...(actions[url] || []), fn];
    },

    async exec() {
      await Promise.all(
        keys(actions).map(async (url) => {
          const $ = await load(url);
          for (const fn of actions[url]) {
            await fn($);
          }
        }),
      );

      const _images: any = {};

      keys(images)
        .sort()
        .forEach((name) => {
          _images[name] = images[name];
        });

      return _images;
    },
  };
};

(async () => {
  const ic = createImageCollector();

  const eachImage = (
    selector: string,
    fn: (src: string, alt: string, $img: Cheerio<Node>) => void | Promise<void>,
    { prefix = "", suffix = ".png" } = {},
  ) => {
    return async ($: CheerioAPI) => {
      const fns: Array<() => void | Promise<void>> = [];

      $(selector).each((_, img) => {
        const $img = $(img);
        const imageName = $img.attr("data-image-name") || $img.attr("alt") || $img.attr("title") || "";
        const src = $img.attr("data-src") || $img.attr("src") || "";

        if (imageName && (src.startsWith("https://") || src.startsWith("//"))) {
          let name = imageName;

          if (suffix && imageName.endsWith(suffix)) {
            name = name.slice(0, name.length - suffix.length).trim();
          }

          if (prefix) {
            if (imageName.startsWith(prefix)) {
              fns.push(() => fn(src, name.slice(prefix.length, name.length).trim(), $img));
            }
          } else {
            fns.push(() => fn(src, name, $img));
          }
        }
      });

      for (const fn of fns) {
        await fn();
      }
    };
  };

  const imgWith = (src: string, suffix: string = "/") => {
    return `${src.split("/latest/")[0]}/latest${suffix}`;
  };

  const fromCategoryPage = (cat: string) =>
    eachImage(".category-page__member img", (src, name) => {
      ic.register(cat, name, imgWith(src, "/scale-to-width-down/256/"));
    });

  ic.page(
    "https://genshin-impact.fandom.com/wiki/Elements",
    eachImage(
      ".wikitable img",
      (src, name) => {
        ic.register("element", name, imgWith(src, "/scale-to-width-down/256/"));
      },
      {
        prefix: "Element",
        suffix: ".svg",
      },
    ),
  );

  ic.page(
    "https://genshin-impact.fandom.com/wiki/Category:Weapon_Type_Icons",
    eachImage(
      ".category-page__member img",
      (src, name) => {
        ic.register(
          "weapon_type",
          {
            polearm: "pole",
            sword: "SWORD_ONE_HAND",
          }[upperCamelCase(name)] || name,
          imgWith(src),
        );
      },
      {
        prefix: "Icon",
        suffix: ".png",
      },
    ),
  );

  ic.page("https://genshin-impact.fandom.com/wiki/Category:Weapons", fromCategoryPage("weapon"));

  ic.page("https://genshin-impact.fandom.com/wiki/Category:Common_Currency", fromCategoryPage("material"));

  ic.page(
    "https://genshin-impact.fandom.com/wiki/Materials",
    eachImage(
      ".item_image img",
      (src, alt) => {
        ic.register("material", alt, imgWith(src, "/scale-to-width-down/256/"));
      },
      {
        prefix: "Item",
      },
    ),
  );

  ic.page(
    "https://genshin-impact.fandom.com/wiki/Artifacts/Sets",
    eachImage(
      ".wikitable img",
      (src, alt) => {
        ic.register("artifact", alt, imgWith(src, "/scale-to-width-down/256/"));
      },
      {
        prefix: "Item",
      },
    ),
  );

  ic.page("https://genshin.honeyhunterworld.com/db/enemy/?lang=EN", ($) => {
    $(".enemy_avatar_cont").each((i, div) => {
      const $div = $(div).find("img").eq(0);
      const $name = $(div).siblings("a").find(".sea_charname").eq(0);
      const src = $div.attr("data-src") || $div.attr("src") || "";
      const name = upperCamelCase($name.text());

      if (name && src) {
        ic.register("enemy", name, `https://genshin.honeyhunterworld.com${src}`);
      }
    });
  });

  const links: { [l: string]: string } = {};

  for (const url of [
    "https://genshin.honeyhunterworld.com/db/char/unreleased-and-upcoming-characters?lang=EN",
    "https://genshin.honeyhunterworld.com/db/char/characters?lang=EN",
  ]) {
    const $1 = await load(url);
    $1(".char_sea_cont").each((i, div) => {
      const $a = $1(div).find("a");
      const href = $a.attr("href")!;
      if (href.startsWith("/db/char/")) {
        const u = href.split("?")[0];
        const char = u.slice("/db/char/".length, u.length - "/".length);
        if (char.indexOf("_boy_") == -1) {
          links[char] = `https://genshin.honeyhunterworld.com${href}`;
        }
      }
    });
  }

  await Promise.all(
    Object.keys(links).map(async (char) => {
      const $ = await load(links[char]);

      const name = trim(($(".custom_title").eq(0).text() || $("title").eq(0).text()).split("|")[0]);

      console.log(`${name}/${char}`);

      if (name == "Traveler") {
        ["geo", "anemo", "electro"].forEach((e) => {
          ic.register(
            "character",
            `${name}_${e}`,
            `https://genshin.honeyhunterworld.com/img/char/${char}_face.png`,
          );
        });
      } else {
        ic.register(
          "character",
          `${name}`,
          `https://genshin.honeyhunterworld.com/img/char/${char}_face.png`,
        );
      }

      $(".itempic").each((i, img) => {
        const src = $(img).attr("data-src") || $(img).attr("src") || "";

        if (src.startsWith("/img/skills")) {
          const title = $(img).parents("tr").find("a").eq(0).text().trim();
          if (!upperCamelCase(title)) {
            console.log(title);
          }
          if (upperCamelCase(title) && src.startsWith("/img/skills/c_")) {
            ic.register("constellation", title, `https://genshin.honeyhunterworld.com${src}`);
          } else {
            ic.register("talent", title, `https://genshin.honeyhunterworld.com${src}`);
          }
        }
      });
    }),
  );

  const images = await ic.exec();

  writeFileSync("images.json", JSON.stringify(images, null, 2));
})();
